﻿using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace zijian666.Converts.Core;

/// <summary>
/// 强制转换,隐式转换帮助类
/// </summary>
public static class ConvertOperatorHelper
{
    /// <summary>
    /// 缓存
    /// </summary>
    private static readonly ConcurrentDictionary<(Type inType, Type outType), object?> _cached = new();

    /// <summary>
    /// 获取隐式转换或强制转换的方法委托
    /// </summary>
    public static Func<object, T>? Get<T>(Type inType) => _cached.GetOrAdd((inType, typeof(T)), CompileMethod) as Func<object, T>;

    /// <summary>
    /// 编译转换方法, 优先级 输出类型的强制转换 > 输出类型的隐式转换 > 输入类型的强制转换 > 输入类型的隐式转换
    /// </summary>
    private static object? CompileMethod((Type inType, Type outType) key)
    {
        var (inType, outType) = key;
        const BindingFlags flags = BindingFlags.Static | BindingFlags.Public;

        var method = outType.GetMethods(flags).Union(inType.GetMethods(flags))
                            .Where(x => x.Name is "op_Explicit" or "op_Implicit")
                            .Where(x => x.ReflectedType == outType && x.GetParameters() is { Length: 1 } && x.GetParameters()[0].ParameterType == inType)
                            .FirstOrDefault();
        if (method is null)
        {
            return null;
        }

        var parameters = method.GetParameters();
        var p = Expression.Parameter(typeof(object), "p");
        var args = Expression.Convert(p, parameters[0].ParameterType);
        var call = Expression.Call(method, args);
        return Expression.Lambda(typeof(Func<,>).MakeGenericType(typeof(object), outType), call, p).Compile();
    }
}
